一些特别棒的面试题[1]

1.平时有遇到过跨域问题吗?
说到跨域问题,就一定要说到同源,什么是同源,相同协议,相同域名,相同端口,即为同源。
不同源之间的通信就会有跨域问题,一般来说是客户端访问服务器,服务器上去配置跨域。
我遇到的跨域问题都是后端去配置一下就可以解决的,比如我前端在用vue的官方推荐异步请求库axios,去请求后端的koa开启的后端服务时,就会遇到跨域的问题,例如koa使用依赖koa-cors就可以,具体的话,就是Access-Control-Allow-Origin: 源名,可以为*或者是特殊的源。或者是传统的maven或者nginx上,也可以很方便的配置跨域。
JSONP有用过吗?JSONP没用过,但是原理貌似是通过js加载一个script DOM标签进来,然后在新的script的src中引入想要执行的代码。
其实跨域问题在后端中也有类似的,只不过是叫做进程间通信,有IPC,RPC等等方式进行进程间通信。

2.下面这段代码最终输出什么?

1
2
3
4
5
6
7
8
9
10
11
let O = function(name){
 this.name = name || 'world';
};
O.prototype.hello = function(){
 return function(){
  console.log('hello ' + this.name);
 };
};
let o = new O;
let hello = o.hello();
hello();

年轻的我的答案是:hello world。
答案显然是不对的,因为这是一道陷阱题,陷阱就在于O.prototype.hello调用后,return的是一个函数,这么做的话,在执行新实例o的hello方法是,this其实已经变成了window。
那么答案是hello undefined吗?
年轻的你又错了,并不是。
而是 hello 。
请注意,是hello ,而不是hello undefined,而是空字符串
原因就在于window.name是事先是有定义的,而且其值为空。
不信的话你可以在控制台打印window.name,返回的是””,你再打印window.yourname试试看,比如window.frank,返回的就是undefined了。
而我在工作中,遇到的更多的常见的像上面一样的工厂函数式的写法是这样的。

1
2
3
4
5
6
7
8
let O = function(name){
 this.name = name || 'world';
};
O.prototype.hello = function(){
  console.log('hello ' + this.name);
};
let o = new O("frank");
let hello = o.hello("frank");

打印结果为:hello frank。
如果不传入frank的话,打印出的是默认值hello world。

3.[“1”,”2”,”3”].map(parseInt)返回的是什么?
A. [1,2,3] B.[“1”,”2”,”3”] C.[1,1,1] D.其他

这特么又是一道陷阱题,还好我之前在看MDN的map函数时,看到过这个陷阱。
正确答案是D:其他。
其他的具体值为多少?[1,NaN,NaN]。
不敢相信吧,为什么不是可爱的[1,2,3]呢?
因为map函数有3个参数,callback,index和array,parseInt有2个参数,string和radix(进制),只传入parseInt到map中的话,会自动忽略第三个参数array,但是不会忽略index,所以就会把0,1,2作为第二个参数传给parseInt。
如果还不明白的话,我们把[“1”,”2”,”3”].map(parseInt)的每一步都拆开来。

1
2
3
parseInt("1",0) 此时将字符"1"转换为O进制数,由于0进制数不存在,所以返回Number类型的1。
parseInt("2",1) 此时将字符"2"转换为1进制数,由于超出进制数1,所以返回NaN。
parseInt("3",2) 此时将字符"3"转换为2进制数,由于超出进制数2,所以返回NaN。

至此,真相大白。
那么常用的非陷阱式map写法是怎样的呢?
像这样:["1","2","3"].map(x=>parseInt(x))
传一个完整的函数进去,有形参,有callback,这样就不会造成因为参数传入错误而造成结果错误了,最后返回一个漂漂亮亮的经由纯函数处理后的新数组回来。

其实这里如果再深入的话,可以再考察纯函数是什么?
纯函数其实就是一个不改变输入,但是可以借助输入,产生一个以输入为原材料,经过加工处理后,输出一个全新的输出的函数,关键在于不改变输入,纯函数是编写redux的reducer必须具备的技能点。

刚才公司的大牛过来,说他从来不用parseInt,他用加号,+”1” 返回1,+”2”返回2。大牛果然大牛,黑科技是真的多。

4.下面代码中“入库”的颜色是?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<ul class="list" id="list">
 <li class="favorite">
  <span>出库</span>
 </li>
 <li class="favorite">
  <span class="highlight">入库</span>
 </li>
</ul>
<style>
#list .favorite:not(#list) .highlight{
 color: red;
}
#list .highlight:nth-of-type(1):nth-last-of-type(1){
 color: blue;
}
</style>

A. red B.blue C.black
我的答案是:我猜一下,可能是A,因为A的权重是最大的,伪类选择器的权值应该比较小吧。
面试官发来一个👍,明天可以来公司面谈吗?
这道题的解答到此为止,因为我是真的真的对CSS不感兴趣,各位看官老爷请原谅我。

5.说下下面两种font-size单位的异同?
em rem

二者的为了保证用户修改字体大小时,保持垂直方向上的字体大小一致。与px不同,二者都是字体计算大小单位,也就是说,需要通过计算得出其大小,转换成px,微信小程序的rpx也是这样,最后还是转换成了px,可能是借鉴了rem的思想吧。
但是em相对于继承来的父元素,rem相对于根元素。听大牛说,rem在国内使用比较多,我自己也觉得rem使用起来更简单,为根元素的font-size赋一个初始值,再配合css的媒体查询,可以动态的改变这个全局的单位,可以说是牵一发而动全身,使用起来非常方便,而em的可阅读性就很差了,有的时候为了算字体大小,需要一级一级找上去,非常不直观。

现代的常用的浏览器,1rem等于16px。
举2个 rem和em例子对比下。
html简写结构如下:

1
2
3
4
5
<html>
<body>
<div></div>
</body>
</html>

rem 例子:

1
2
3
html { font-size:62.5%; }  /* =10px */
body { font-size: 2.0rem; } /* =20px */
div { font-size: 1.0rem; } /* =10px */

em 例子:

1
2
3
html { font-size:62.5%; }  /* =10px */
body { font-size: 2.0em; } /* =20px */
div { font-size: 1.0em; } /* =20px */

MDN的font-size章节给出了em和rem的非常好的解释,英文原版非常直观,我这里再贴一下:

em
Represents the calculated font-size of the element. If used on the font-size property itself, it represents the inherited font-size of the element.
rem
Represents the font-size of the root element (typically ). When used within the root element font-size, it represents its initial value (a common browser default is 16px, but user-defined preferences may modify this).

其实em和rem与MVVM框架的组件间通信有些类似,都有逐级继承和全局影响的概念。em是逐级传递的,也就是继承,框架中用props和事件订阅发布的方式也是这样,爷,父,孙的传递都是要一级一级去传递的,爷爷想直接传授点技能给孙子必须先传授给父亲,爷爷→父亲→孙子;而rem就和框架中的状态管理库很像,例如vuex和redux,抽取出一个全局的状态树,不用一级一级的去很复杂的去继承,爷爷想教教孙子,直接就可以传授给孙子,爷爷→孙子

6.只用一个div 实现定时红绿灯
default

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<title>仅用一个DIV实现红绿灯</title>
<style>
@keyframes light{
0%{
background-color: green;
left: 0;
}
33.3%{
background-color: green;
left: 0;
}
33.4%{
background-color: yellow;
left: 200px;
}
66.6%{
background-color: yellow;
left: 200px;
}
66.7%{
background-color: red;
left: 400px;
}
99.9%{
background-color: red;
left: 400px;
}
}
.traffic-light{
position: relative;
width: 200px;
height: 200px;
border-radius: 50%;
animation: light 3s ease-in-out 0s infinite;
}
.container{
width: 600px;
border:10px solid #000;
border-radius: 20% 20%;
}
</style>
</head>
<body>
<div class="container">
<div class="traffic-light"></div>
</div>
</body>
</html>